package com.such.kit.el;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import com.such.kit.Callable;
import com.such.kit.Logger;
import com.such.kit.cache.CacheCommons;
import com.such.kit.el.support.ELFunction;
import com.such.kit.el.support.ExpressionEngine;
import com.such.kit.string.StringCommons;
import com.such.kit.validate.ValidateCommons;

/**
 * @author SUCH
 * <pre>
 * EL 表达式通用工具
 * </pre>
 */
public class ExpressionCommons {

	public static String ENGINE_TYPE_MVEL = "MVEL";

	private static String engineCache = CacheCommons.buildCacheName(ExpressionCommons.class);

	private String engineType;
	private ExpressionEngine expressionEngine;
	private List<ELFunction> functions = new ArrayList<ELFunction>();

	private ExpressionCommons(String engineType, ExpressionEngine expressionEngine) {
		this.engineType = engineType;
		this.expressionEngine = expressionEngine;
	}

	/**
	 * <pre>
	 * 获取 ExpressionCommons 实例
	 * </pre>
	 * @param engineType 引擎类型
	 * @return ExpressionCommons 实例
	 */
	public static ExpressionCommons getInstance(final String engineType) {
		ExpressionCommons expressionCommons = null;
		try {
			expressionCommons = CacheCommons.get(engineCache, engineType, new Callable<String, ExpressionCommons>() {
				@Override
				public ExpressionCommons call(String param) throws Exception {
					ExpressionCommons expressionCommons = null;
					if (ENGINE_TYPE_MVEL.equals(engineType)) {
						expressionCommons = new ExpressionCommons(engineType, new MvelExpressionEngine());
					}
					return expressionCommons;
				}
			});
		} catch (Exception e) {
			Logger.error(ExpressionCommons.class, "创建 ExpressionCommons 异常", e);
		}
		return expressionCommons;
	}

	/**
	 * <pre>
	 * 执行表达式
	 * </pre>
	 * @param expression 表达式
	 * @param inputs 入参
	 * @return 执行结果
	 * @throws Exception
	 */
	public <Return, Input> Return execute(String expression, Map<String, Input> inputs) throws Exception {
		return this.expressionEngine.execute(expression, inputs);
	}

	/**
	 * <pre>
	 * 将 clazz 中标记了 ELFunction 的方法注册为函数
	 * </pre>
	 * @param <T>
	 * @param clazz 函数类
	 */
	public <T> void registers(Class<T> clazz) {
		Method[] methods = clazz.getMethods();
		for (Method method : methods) {
			ELFunction function = method.getAnnotation(ELFunction.class);
			if (ValidateCommons.isEmpty(function)) {
				function = clazz.getAnnotation(ELFunction.class);
			}
			if (ValidateCommons.isNotEmpty(function)) {
				function = fillELFunction(function, clazz, method);
				this.functions.add(function);
				String functionName = method.getName();
				String namespace = function.namespace();
				if (ValidateCommons.isNotEmpty(namespace)) {
					functionName = StringCommons.merger(namespace, ELFunction.NAMESPACE_SEPARATE, method.getName());
				}
				if (ENGINE_TYPE_MVEL.equals(this.engineType)) {
					this.expressionEngine.register(functionName, method);
				}
			}
		}
	}

	public List<ELFunction> getELFunctions() {
		return this.functions;
	}

	private <T> ELFunction fillELFunction(ELFunction function, Class<T> clazz, Method method) {
		final String namespace = function.namespace();
		final String type = function.type();
		final String name = StringCommons.emptyValue(function.name(), method.getName(), function.name());
		final String descr = function.descr();
		final String usage = StringCommons.emptyValue(function.usage(), StringCommons.merger(method.getName(), "(", StringCommons.merger(method.getParameterTypes(), ", ", true), ")"), function.usage());
		final String[] scopes = function.scopes();
		final int priority = function.priority();
		return new ELFunction() {

			@Override
			public Class<? extends Annotation> annotationType() {
				return ELFunction.class;
			}

			@Override
			public String namespace() {
				return namespace;
			}

			@Override
			public String type() {
				return type;
			}

			@Override
			public String name() {
				return name;
			}

			@Override
			public String descr() {
				return descr;
			}

			@Override
			public String usage() {
				return usage;
			}

			@Override
			public String[] scopes() {
				return scopes;
			}

			@Override
			public int priority() {
				return priority;
			}
		};
	}
}
